#ifndef RS_RESPELL_H
#define RS_RESPELL_H

#include <vector>
#include <map>
#include "rs_string.h"
#include "rs_convert.h"
using namespace std;


class rs_respell;

class rs_command {
    // available command from command-line
public:
    rs_command (
        bool (*a_run)(),
        const rs_string& group_desc,
        const char* a_desc, // co_name \r co_arg_v \r co_help1 \r co_help
        // co_arg_v and co_help are multi-line strings, co_help has last line without \n
        // each argument (one of co_arg_v) is:
        // name opt_slash_type colon default_value colon description
        //   name: name of argument
        //   opt_slash_type:
        //       if argument is textual, this is slash-t "/t", 
        //       if argument is numerical, this is slash-n "/n", 
        //       if argument is logical, this is slash-b "/b", (boolean)
        //       otherwise empty (default, textual)
        //   colon: ":"
        //   default_value: string representation of default value
        //   colon: ":"
        //   description: help text (one line) for this arument
        bool hidden=false
        );
    const rs_string& get_name() const { return co_name; };
    bool is_hidden() const { return co_hidden; };
    const rs_string& get_group() const { return co_group; };
    const rs_string& get_help_summary() const { return co_help1; };
    const rs_string& get_help_details() const { return co_help; };
    const rs_string_v& get_arg_v() const { return co_arg_v; };
    const rs_string_v& get_arg_name_v() const { return co_arg_name_v; };
    const rs_string_v& get_arg_type_v() const { return co_arg_type_v; };
    const rs_string_v& get_arg_def_v() const { return co_arg_def_v; };
    const rs_string_v& get_arg_help_v() const { return co_arg_help_v; };
    const bool run() const { return (*co_run)(); };
    static rs_respell& get_appl() { return *co_appl; };
private:
    static rs_respell* co_appl;
    bool (*co_run)(); // pointer to a running function; returns true if successful
    rs_string co_name;  // command name
    bool co_hidden;
    const rs_string& co_group;
    rs_string_v co_arg_v; // argument specifications
    rs_string_v co_arg_name_v, co_arg_type_v, co_arg_def_v, co_arg_help_v;
    // argument names, types, default values, and help description
    rs_string co_help1; // one-line help string, 70 characters max
    rs_string co_help;  // complete help, no examples though
    void parse_argument (const rs_string& arg,
        rs_string& arg_name,
        rs_string& arg_type,
        rs_string& arg_def,
        rs_string& arg_help);
};


class rs_respell {
    // all the commands and some variables holding value over several command calls
public:
    rs_respell ();
    void add_cmd (const rs_command* cmd) {
        m_commands.push_back (cmd);
    };
    int run_all(const rs_string_v& arg_v);
    bool run_batch (const rs_string& batch_fname);
    bool cmd_line_help (const rs_string& arg);
    rs_string& t (const rs_string& arg_name);
    unsigned&  n (const rs_string& arg_name);
    converter_factory m_conversions;
    rs_string m_test_ref_dir;
    int m_test_cnt;   // number of all tests executed
    int m_failed_cnt; // number of tests that failed
private:
    void output_help_details (const rs_command *cmd) const;
    const rs_command* find_cmd (const rs_string& cmd_name);
    void run_cmd (const rs_string_v& arg_v, int& result, bool& was_processed);
    int rs_dispatcher(const rs_string_v& arg_v);
    bool parse_and_execute_line (const rs_string& cmd_line);
    bool parse_args (const rs_string_v& arg_v, const rs_command* cmd);
    void add_arg_value (const rs_string& arg_name, const rs_string& arg_type, const rs_string& arg_value);

    vector<const rs_command*> m_commands;
    map<rs_string, rs_string> m_arg_t; // textual arguments: name and value
    map<rs_string, unsigned>  m_arg_n; // numeric arguments: name and value
    rs_string m_tt; // dummy value for incorrect textual arguments
    unsigned m_nn;  // dummy value for incorrect numeric arguments
};


#endif

